home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / octa209s.zip / octave-2.09 / src / oct-hist.cc < prev    next >
Text File  |  1997-03-15  |  12KB  |  590 lines

  1. /*
  2.  
  3. Copyright (C) 1996 John W. Eaton
  4.  
  5. This file is part of Octave.
  6.  
  7. Octave is free software; you can redistribute it and/or modify it
  8. under the terms of the GNU General Public License as published by the
  9. Free Software Foundation; either version 2, or (at your option) any
  10. later version.
  11.  
  12. Octave is distributed in the hope that it will be useful, but WITHOUT
  13. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License
  18. along with Octave; see the file COPYING.  If not, write to the Free
  19. Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  20.  
  21. The functions listed below were adapted from similar functions from
  22. GNU Bash, the Bourne Again SHell, copyright (C) 1987, 1989, 1991 Free
  23. Software Foundation, Inc.
  24.  
  25.   do_history         edit_history_readline
  26.   do_edit_history    edit_history_add_hist
  27.  
  28. */
  29.  
  30. /* Modified by Klaus Gebhardt, 1996 */
  31.  
  32. #ifdef HAVE_CONFIG_H
  33. #include <config.h>
  34. #endif
  35.  
  36. #include <csignal>
  37. #include <cstdlib>
  38. #include <cstring>
  39.  
  40. #include <string>
  41.  
  42. #include <fstream.h>
  43.  
  44. #ifdef HAVE_UNISTD_H
  45. #ifdef HAVE_SYS_TYPES_H
  46. #include <sys/types.h>
  47. #endif
  48. #include <unistd.h>
  49. #endif
  50.  
  51. #include "cmd-hist.h"
  52. #include "file-ops.h"
  53. #include "str-vec.h"
  54.  
  55. #include <defaults.h>
  56. #include "defun.h"
  57. #include "error.h"
  58. #include "input.h"
  59. #include "oct-hist.h"
  60. #include "oct-obj.h"
  61. #include "pager.h"
  62. #include "sighandlers.h"
  63. #include "sysdep.h"
  64. #include "toplev.h"
  65. #include "unwind-prot.h"
  66. #include "utils.h"
  67. #include "variables.h"
  68.  
  69. #ifdef __EMX__
  70. extern "C"
  71. {
  72. #include <process.h>
  73.   int _IO_system (const char *, int);
  74. }
  75. #endif
  76.  
  77. // Nonzero means input is coming from temporary history file.
  78. int input_from_tmp_history_file = 0;
  79.  
  80. // Guess what?
  81. command_history octave_command_history;
  82.  
  83. // Get some default values, possibly reading them from the
  84. // environment.
  85.  
  86. int
  87. default_history_size (void)
  88. {
  89.   int size = 1024;
  90.   char *env_size = getenv ("OCTAVE_HISTSIZE");
  91.   if (env_size)
  92.     {
  93.       int val;
  94.       if (sscanf (env_size, "%d", &val) == 1)
  95.     size = val > 0 ? val : 0;
  96.     }
  97.   return size;
  98. }
  99.  
  100. string
  101. default_history_file (void)
  102. {
  103.   string file;
  104.  
  105.   char *env_file = getenv ("OCTAVE_HISTFILE");
  106.  
  107.   if (env_file)
  108.     {
  109.       fstream f (env_file, (ios::in | ios::out));
  110.  
  111.       if (f)
  112.     {
  113.       file = env_file;
  114.       f.close ();
  115.     }
  116.     }
  117.  
  118.   if (file.empty ())
  119.     {
  120.       if (! Vhome_directory.empty ())
  121.     {
  122.       file = Vhome_directory;
  123.       file.append ("/.octave_hist");
  124.     }
  125.       else
  126.     file = ".octave_hist";
  127.     }
  128.  
  129.   return file;
  130. }
  131.  
  132. // Display, save, or load history.  Stolen and modified from bash.
  133. //
  134. // Arg of -w FILENAME means write file, arg of -r FILENAME
  135. // means read file, arg of -q means don't number lines.  Arg of N
  136. // means only display that many items. 
  137.  
  138. static void
  139. do_history (int argc, const string_vector& argv)
  140. {
  141.   int numbered_output = 1;
  142.  
  143.   int i;
  144.   for (i = 1; i < argc; i++)
  145.     {
  146.       string option = argv[i];
  147.  
  148.       if (option == "-r" || option == "-w" || option == "-a"
  149.       || option == "-n")
  150.     {
  151.       if (i < argc - 1)
  152.         {
  153.           string file = oct_tilde_expand (argv[i+1]);
  154.           octave_command_history.set_file (file);
  155.         }
  156.  
  157.       if (option == "-a")
  158.         // Append `new' lines to file.
  159.         octave_command_history.append ();
  160.  
  161.       else if (option == "-w")
  162.         // Write entire history.
  163.         octave_command_history.write ();
  164.  
  165.       else if (option == "-r")
  166.         // Read entire file.
  167.         octave_command_history.read ();
  168.  
  169.       else if (option == "-n")
  170.         // Read `new' history from file.
  171.         octave_command_history.read_range ();
  172.  
  173.       else
  174.         panic_impossible ();
  175.  
  176.       return;
  177.     }
  178.       else if (argv[i] == "-q")
  179.     numbered_output = 0;
  180.       else if (argv[i] == "--")
  181.     {
  182.       i++;
  183.       break;
  184.     }
  185.       else
  186.     break;
  187.     }
  188.  
  189.   int limit = -1;
  190.  
  191.   if (i < argc)
  192.     {
  193.       if (sscanf (argv[i].c_str (), "%d", &limit) != 1)
  194.         {
  195.       if (argv[i][0] == '-')
  196.         error ("history: unrecognized option `%s'", argv[i].c_str ());
  197.       else
  198.         error ("history: bad non-numeric arg `%s'", argv[i].c_str ());
  199.  
  200.       return;
  201.         }
  202.  
  203.       if (limit < 0)
  204.     limit = -limit;
  205.     }
  206.  
  207.   string_vector hlist = octave_command_history.list (limit, numbered_output);
  208.  
  209.   int len = hlist.length ();
  210.  
  211.   for (i = 0; i < len; i++)
  212.     octave_stdout << hlist[i] << "\n";
  213. }
  214.  
  215. // Read the edited history lines from STREAM and return them
  216. // one at a time.  This can read unlimited length lines.  The
  217. // caller should free the storage.
  218.  
  219. static char *
  220. edit_history_readline (fstream& stream)
  221. {
  222.   char c;
  223.   int line_len = 128;
  224.   int lindex = 0;
  225.   char *line = new char [line_len];
  226.   line[0] = '\0';
  227.  
  228.   while (stream.get (c))
  229.     {
  230.       if (lindex + 2 >= line_len)
  231.     {
  232.       char *tmp_line = new char [line_len += 128];
  233.       strcpy (tmp_line, line);
  234.       delete [] line;
  235.       line = tmp_line;
  236.     }
  237.  
  238.       if (c == '\n')
  239.     {
  240.       line[lindex++] = '\n';
  241.       line[lindex++] = '\0';
  242.       return line;
  243.     }
  244.       else
  245.     line[lindex++] = c;
  246.     }
  247.  
  248.   if (! lindex)
  249.     {
  250.       delete [] line;
  251.       return 0;
  252.     }
  253.  
  254.   if (lindex + 2 >= line_len)
  255.     {
  256.       char *tmp_line = new char [lindex+3];
  257.       strcpy (tmp_line, line);
  258.       delete [] line;
  259.       line = tmp_line;
  260.     }
  261.  
  262.   // Finish with newline if none in file.
  263.  
  264.   line[lindex++] = '\n';
  265.   line[lindex++] = '\0';
  266.   return line;
  267. }
  268.  
  269. // Use `command' to replace the last entry in the history list, which,
  270. // by this time, is `run_history blah...'.  The intent is that the
  271. // new command becomes the history entry, and that `fc' should never
  272. // appear in the history list.  This way you can do `run_history' to
  273. // your heart's content.
  274.  
  275. static void
  276. edit_history_repl_hist (const string& command)
  277. {
  278.   if (! command.empty ())
  279.     {
  280.       string_vector hlist = octave_command_history.list ();
  281.  
  282.       int len = hlist.length ();
  283.  
  284.       if (len > 0)
  285.     {
  286.       int i = len - 1;
  287.  
  288.       string histent = octave_command_history.get_entry (i);
  289.  
  290.       if (! histent.empty ())
  291.         {
  292.           string cmd = command;
  293.  
  294.           int cmd_len = cmd.length ();
  295.  
  296.           if (cmd[cmd_len - 1] == '\n' || cmd[cmd_len - 1] == '\r'
  297.           || cmd[cmd_len - 1] == '')
  298.         cmd.resize (cmd_len - 1);
  299.  
  300.           if (! cmd.empty ())
  301.         octave_command_history.replace_entry (i, cmd);
  302.         }
  303.     }
  304.     }
  305. }
  306.  
  307. static void
  308. edit_history_add_hist (const string& line)
  309. {
  310.   if (! line.empty ())
  311.     {
  312.       string tmp = line;
  313.  
  314.       int len = tmp.length ();
  315.     
  316.       if (len > 0 &&
  317.       (tmp[len-1] == '\n' || tmp[len-1] == '\r' || tmp[len-1] == ''))
  318.     tmp.resize (len - 1);
  319.  
  320.       if (! tmp.empty ())
  321.     octave_command_history.add (tmp);
  322.     }
  323. }
  324.  
  325. static string
  326. mk_tmp_hist_file (int argc, const string_vector& argv,
  327.           int insert_curr, char *warn_for) 
  328. {
  329.   string retval;
  330.  
  331.   string_vector hlist = octave_command_history.list ();
  332.  
  333.   int hist_count = hlist.length ();
  334.  
  335.   // The current command line is already part of the history list by
  336.   // the time we get to this point.  Delete it from the list.
  337.  
  338.   hist_count -= 2;
  339.  
  340.   if (! insert_curr)
  341.     octave_command_history.remove (hist_count);
  342.  
  343.   hist_count--;
  344.  
  345.   // If no numbers have been specified, the default is to edit the
  346.   // last command in the history list.
  347.  
  348.   int hist_end = hist_count;
  349.   int hist_beg = hist_count;
  350.   int reverse = 0;
  351.  
  352.   // Process options.
  353.  
  354.   int usage_error = 0;
  355.   if (argc == 3)
  356.     {
  357.       if (sscanf (argv[1].c_str (), "%d", &hist_beg) != 1
  358.       || sscanf (argv[2].c_str (), "%d", &hist_end) != 1)
  359.     usage_error = 1;
  360.       else
  361.     {
  362.       hist_beg--;
  363.       hist_end--;
  364.     }
  365.     }
  366.   else if (argc == 2)
  367.     {
  368.       if (sscanf (argv[1].c_str (), "%d", &hist_beg) != 1)
  369.     usage_error = 1;
  370.       else
  371.     {
  372.       hist_beg--;
  373.       hist_end = hist_beg;
  374.     }
  375.     }
  376.  
  377.   if (hist_beg < 0 || hist_end < 0 || hist_beg > hist_count
  378.       || hist_end > hist_count)
  379.     {
  380.       error ("%s: history specification out of range", warn_for);
  381.       return retval;
  382.     }
  383.  
  384.   if (usage_error)
  385.     {
  386.       usage ("%s [first] [last]", warn_for);
  387.       return retval;
  388.     }
  389.  
  390.   if (hist_end < hist_beg)
  391.     {
  392.       int t = hist_end;
  393.       hist_end = hist_beg;
  394.       hist_beg = t;
  395.       reverse = 1;
  396.     }
  397.  
  398.   string name = oct_tempnam ();
  399.  
  400.   fstream file (name.c_str (), ios::out);
  401.  
  402.   if (! file)
  403.     {
  404.       error ("%s: couldn't open temporary file `%s'", warn_for,
  405.          name.c_str ());
  406.       return retval;
  407.     }
  408.  
  409.   if (reverse)
  410.     {
  411.       for (int i = hist_end; i >= hist_beg; i--)
  412.     file << hlist[i] << "\n";
  413.     }
  414.   else
  415.     {
  416.       for (int i = hist_beg; i <= hist_end; i++)
  417.     file << hlist[i] << "\n";
  418.     }
  419.  
  420.   file.close ();
  421.  
  422.   return name;
  423. }
  424.  
  425. static void
  426. do_edit_history (int argc, const string_vector& argv)
  427. {
  428.   string name = mk_tmp_hist_file (argc, argv, 0, "edit_history");
  429.  
  430.   if (name.empty ())
  431.     return;
  432.  
  433.   // Call up our favorite editor on the file of commands.
  434.  
  435.   string cmd = Veditor;
  436.   cmd.append (" ");
  437.   cmd.append (name);
  438.  
  439.   // Ignore interrupts while we are off editing commands.  Should we
  440.   // maybe avoid using system()?
  441.  
  442.   volatile octave_interrupt_handler old_interrupt_handler
  443.     = octave_ignore_interrupts ();
  444.  
  445. #ifdef __EMX__
  446.   _IO_system (cmd.c_str (), P_WAIT);
  447. #else
  448.   system (cmd.c_str ());
  449. #endif
  450.  
  451.   octave_set_interrupt_handler (old_interrupt_handler);
  452.  
  453.   // Write the commands to the history file since parse_and_execute
  454.   // disables command line history while it executes.
  455.  
  456.   fstream file (name.c_str (), ios::in);
  457.  
  458.   char *line;
  459.   int first = 1;
  460.   while ((line = edit_history_readline (file)) != 0)
  461.     {
  462.       // Skip blank lines.
  463.  
  464.       if (line[0] == '\n')
  465.     {
  466.       delete [] line;
  467.       continue;
  468.     }
  469.  
  470.       if (first)
  471.     {
  472.       first = 0;
  473.       edit_history_repl_hist (line);
  474.     }
  475.       else
  476.     edit_history_add_hist (line);
  477.     }
  478.  
  479.   file.close ();
  480.  
  481.   // Turn on command echo, so the output from this will make better
  482.   // sense.
  483.  
  484.   begin_unwind_frame ("do_edit_history");
  485.   unwind_protect_int (Vecho_executing_commands);
  486.   unwind_protect_int (input_from_tmp_history_file);
  487.   Vecho_executing_commands = ECHO_CMD_LINE;
  488.   input_from_tmp_history_file = 1;
  489.  
  490.   parse_and_execute (name, 1);
  491.  
  492.   run_unwind_frame ("do_edit_history");
  493.  
  494.   // Delete the temporary file.  Should probably be done with an
  495.   // unwind_protect.
  496.  
  497.   unlink (name.c_str ());
  498. }
  499.  
  500. static void
  501. do_run_history (int argc, const string_vector& argv)
  502. {
  503.   string name = mk_tmp_hist_file (argc, argv, 1, "run_history");
  504.  
  505.   if (name.empty ())
  506.     return;
  507.  
  508.   // Turn on command echo so the output from this will make better
  509.   // sense.
  510.  
  511.   begin_unwind_frame ("do_run_history");
  512.   unwind_protect_int (Vecho_executing_commands);
  513.   unwind_protect_int (input_from_tmp_history_file);
  514.   Vecho_executing_commands = ECHO_CMD_LINE;
  515.   input_from_tmp_history_file = 1;
  516.  
  517.   parse_and_execute (name, 1);
  518.  
  519.   run_unwind_frame ("do_run_history");
  520.  
  521.   // Delete the temporary file.
  522.  
  523.   // XXX FIXME XXX -- should probably be done using an unwind_protect.
  524.  
  525.   unlink (name.c_str ());
  526. }
  527.  
  528. DEFUN_TEXT (edit_history, args, ,
  529.   "edit_history [first] [last]\n\
  530. \n\
  531. edit commands from the history list")
  532. {
  533.   octave_value_list retval;
  534.  
  535.   int argc = args.length () + 1;
  536.  
  537.   string_vector argv = args.make_argv ("edit_history");
  538.  
  539.   if (error_state)
  540.     return retval;
  541.  
  542.   do_edit_history (argc, argv);
  543.  
  544.   return retval;
  545. }
  546.  
  547. DEFUN_TEXT (history, args, ,
  548.   "history [N] [-w file] [-r file] [-q]\n\
  549. \n\
  550. display, save, or load command history")
  551. {
  552.   octave_value_list retval;
  553.  
  554.   int argc = args.length () + 1;
  555.  
  556.   string_vector argv = args.make_argv ("history");
  557.  
  558.   if (error_state)
  559.     return retval;
  560.  
  561.   do_history (argc, argv);
  562.  
  563.   return retval;
  564. }
  565.  
  566. DEFUN_TEXT (run_history, args, ,
  567.   "run_history [first] [last]\n\
  568. \n\
  569. run commands from the history list")
  570. {
  571.   octave_value_list retval;
  572.  
  573.   int argc = args.length () + 1;
  574.  
  575.   string_vector argv = args.make_argv ("run_history");
  576.  
  577.   if (error_state)
  578.     return retval;
  579.  
  580.   do_run_history (argc, argv);
  581.  
  582.   return retval;
  583. }
  584.  
  585. /*
  586. ;;; Local Variables: ***
  587. ;;; mode: C++ ***
  588. ;;; End: ***
  589. */
  590.